using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using ActiveSharp.PropertyMapping;
using NUnit.Framework;

namespace ActiveSharp.Tests
{
    /// <summary>
    /// Shows how to use ActiveSharp when your properties don't actually have backing fields, 
    /// but rather delegate to another object.  The solution is a workaround - it simply creates
    /// unused backing "tags" for each property.  They are like backing fields, but they are not used for anything
    /// other than helping ActiveSharp to identify the property.
    /// * NOTE: This is not the standard usage pattern for ActiveSharp; only
    ///   do it if you know you need to. * 
    /// </summary>
    /// <remarks>There is one problem with this approach - it _always_ thinks that the property has
    /// changed, even if it just simply been set to the same value.
    /// </remarks>
    [TestFixture]
    public class WrapperObjectTests
    {
        [Test]
        public void CanDetectPropertyChange()
        {
            TestClass t = new TestClass();

            t.Foo = "Abc";
            Assert.AreEqual("Foo", t.LastChangedProperty.Name);

            t.Bar = 5;
            Assert.AreEqual("Bar", t.LastChangedProperty.Name);
        }

        public abstract class BaseTestClass
        {
            protected void Notify<T>(ref T field)
            {
                LastChangedProperty = PropertyMap.GetProperty(this, ref field);
                // do what ever you want, e.g. notify that the property has changed...
            }

            private PropertyInfo _lastChangedProperty;

            public PropertyInfo LastChangedProperty
            {
                get { return _lastChangedProperty; }
                private set { _lastChangedProperty = value; }
            }
        }

        /// <summary>
        /// An empty struct.  Doesn't do anything other than serve as a target for ActiveSharp's
        /// offset calculations.
        /// </summary>
        /// <remarks>Isn't actually 0-sized.  Compiler still makes it take up one byte (same as 
        /// a boolean does) for the reason described here: http://bytes.com/forum/thread497013.html
        /// and here: http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=355187 </remarks>
        public struct Tag
        {}

        /// <summary>
        /// The outer class, from which we want to raise notifications
        /// </summary>
        public class TestClass : BaseTestClass
        {
            private ClassThatStoresTheRealData _wrappedObject = new ClassThatStoresTheRealData();

            // the "tags" that let ActiveSharp identify the properties
            // You could use any type, because they are never really used (except to calculate "offsets" inside ActiveSharp)
            // I have used the "Tag" type defined above, to make the meaning clear, and because at 1 byte 
            // in size, Tag is just as compact as anything else is.
            Tag _foo, 
                 _bar;

            public string Foo
            {
                get { return _wrappedObject.Fooee; }
                set
                {
                    Notify(ref _foo);
                    _wrappedObject.Fooee = value;
                }
            }

            public int Bar
            {
                get { return _wrappedObject.Baaa; }
                set
                {
                    Notify(ref _bar);
                    _wrappedObject.Baaa = value;
                }
            }
        }

        /// <summary>
        /// This is some inner, wrapped class
        /// </summary>
        public class ClassThatStoresTheRealData
        {
            private string _fooee;
            private int _baaa;

            public string Fooee
            {
                get { return _fooee;  }
                set { _fooee = value; }
            }

            public int Baaa
            {
                get { return _baaa;  }
                set { _baaa = value; }
            }
        }
    }
}
